package chances.epg.ui.taglib.json;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.BodyTagSupport;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;

import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Strings;

public class JsonTag extends BodyTagSupport {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	protected String var;
	private boolean array;
	private String clazz;
	private String encode;
	private int cache;
	private String file;
	ObjectMapper mapper = new ObjectMapper();

	@Override
	public int doEndTag() throws JspException {
		Object value = null;
		if (StringUtils.isNotEmpty(file)) {
			 value = loadFromFile();
		} else {
			 value = loadFromBody();
		}
		pageContext.setAttribute(this.var, value);
		this.reset();
		return  super.doEndTag();
	}
	protected Object loadFromBody() throws JspException {
		String bodyString = this.bodyContent.getString();
		if (StringUtils.isNotEmpty(bodyString)) {
			if (StringUtils.isNotEmpty(encode)) {
				try {
					if (this.encode != null) {
						bodyString = new String(bodyString.getBytes(),
								this.encode);
					}
					;
				} catch (UnsupportedEncodingException e) {
					throw new JspException(e);
				}
			}
		} else {
			if(this.array){
				bodyString = "[]";
			}else{
				bodyString = "{}";
			}
		}
		return this.toJson(bodyString);
	}

	protected Object loadFromFile() throws JspException {
		try {
			String realPath = pageContext.getServletContext().getRealPath(
					file);
			File aFile = new File(realPath);
			Object value = this.loadFormCache(aFile);
			if(value != null){
				pageContext.setAttribute(this.var, value);
			}
			String content = null;
			if (Strings.isNullOrEmpty(this.encode)) {
				content = IOUtils.toString(aFile.toURI());
			} else {
				content = IOUtils.toString(aFile.toURI(), encode);
			}
		
			return this.toJson(content);
		} catch (IOException e) {
			throw new JspException(e);
		}
	}
	/**
	 * 检查Cache是不是有数据
	 * @param file
	 * @return
	 */
	public Object loadFormCache(File file) {
		String key = "json." + file.hashCode();
		String timeKey = "json." + file.hashCode() + ".time";
		Object value = pageContext.getAttribute(key,
				PageContext.APPLICATION_SCOPE);
		if (value == null) {
			return value;
		} else {
			Long timeStamp = (Long) pageContext.getAttribute(timeKey,
					PageContext.APPLICATION_SCOPE);
			if (timeStamp != null
					&& timeStamp.longValue() == file.lastModified()) {
				return value;
			} else {
				pageContext.removeAttribute(key, PageContext.APPLICATION_SCOPE);
				return null;
			}
		}

	}
	/**
	 * 保存到缓存中
	 * @param file
	 * @param value
	 */
	public void saveCache(File file, Object value) {
		String key = "json." + file.hashCode();
		String timeKey = "json." + file.hashCode() + ".time";
		long time = file.lastModified();
		pageContext.setAttribute(timeKey, new Long(time),
				PageContext.APPLICATION_SCOPE);
		pageContext.setAttribute(key, value, PageContext.APPLICATION_SCOPE);
	}

	public Object toJson(String content) throws JspException {
		Object value = null;
		if (Strings.isNullOrEmpty(clazz)) {
			value = toMap(content);
		} else {
			value = toEntry(content);
		}
		return value;
	}

	private void reset() {
		this.var = null;
		this.array = false;
		this.cache = 0;
		this.clazz = null;
		this.file = null;

	}

	@Override
	public void release() {
		reset();
		super.release();
	}
	public Object toEntry(String bodyString) throws JspException {
		try {
			Class<?> entryClass = Class.forName(this.clazz);
			if (this.isArray()) {
				JavaType javaType = mapper.getTypeFactory()
						.constructParametrizedType(List.class, List.class,
								entryClass);
				Object result = mapper.readValue(bodyString, javaType);
				return result;

			} else {
				Object result = mapper.readValue(bodyString, entryClass);
				return result;
			}
		} catch (Exception e) {
			throw new JspException(e);
		}
	}

	public Object toMap(String bodyString) throws JspException {
		try {
			if (this.isArray()) {
				Object result = mapper.readValue(bodyString, ArrayList.class);
				pageContext.setAttribute(this.var, result);
				return result;

			} else {
				@SuppressWarnings("unchecked")
				Map<String, Object> result = mapper.readValue(bodyString,
						Map.class);
				return result;
			}
		} catch (Exception e) {
			throw new JspException(e);
		}
	}

	public String getVar() {
		return var;
	}

	public boolean isArray() {
		return array;
	}

	public String getClazz() {
		return clazz;
	}

	public void setVar(String var) {
		this.var = var;
	}

	public void setArray(boolean array) {
		this.array = array;
	}

	public void setClazz(String clazz) {
		this.clazz = clazz;
	}

	public String getEncode() {
		return encode;
	}

	public int getCache() {
		return cache;
	}

	public void setCache(int cache) {
		this.cache = cache;
	}

	public void setEncode(String encode) {
		this.encode = encode;
	}

	public String getFile() {
		return file;
	}
	public void setFile(String file) {
		this.file = file;
	}
}
